#include "CrossSectionNumericalCalculator.h"

#include <algorithm>
#include <vector>

#include "DataStructure/Common/Real.h"

#include "SectionCalculator/StandardSectionTool.h"

using SG::Algebra::Point;
using SG::DataStructure::Common::Real;
using SG::DataStructure::FEM::PBARLData;
using SG::FEMSolver::Element::CrossSectionNumericalCalculator;

void CrossSectionNumericalCalculator::Mesh (const PBARLData& section, _OUT Meshes& meshes, int refineLevel) const
{
    Real w{ section.m_dimension[0] };
    Real t1{ section.m_dimension[1] };
    Real h{ section.m_dimension[2] };
    Real t2{ section.m_dimension[3] };

    std::vector<Real> edge{ t1, t2, 0.5 * w, 0.5 * (h - t2) };
    Real              meshSize = *min_element (edge.begin (), edge.end ()) / (refineLevel + 1);
    int               Na1      = int (t1 / meshSize);
    int               Na2      = int (0.5 * w / (meshSize * 2));
    Na2                        = std::max (Na2, 1);
    int Nb1                    = int (t2 / meshSize);
    int Nb2                    = int (0.5 * (h - t2) / (meshSize * 2));
    Nb2                        = std::max (Nb2, 1);

    StandardSectionTool secMesh;
    Point               point;
    int                 nodeId{ 0 };
    int                 eleId{ 0 };
    Meshes              newMeshes;
    NodeSet             newEdgeNodes;
    int                 numRepeatNodes{ 0 };
    NodeSet             edgeNodes;

    // 1st block
    point.m_y = 0.5 * w;
    point.m_z = 0.0;
    secMesh.MeshRect2 (point, t1, 0.5 * (h - t2), Na1, Nb2, nodeId, eleId, _OUT newMeshes, _OUT newEdgeNodes);
    secMesh.Merge (edgeNodes, numRepeatNodes, newMeshes, meshes, nodeId, eleId);
    secMesh.UpdateEdgeNodes (newEdgeNodes, numRepeatNodes, edgeNodes);

    // 2nd block
    point.m_z = 0.5 * (h + t2);
    secMesh.MeshRect2 (point, t1, -t2, Na1, Nb1, nodeId, eleId, _OUT newMeshes, _OUT newEdgeNodes);
    numRepeatNodes = 2 * Na1 + 1;
    secMesh.Merge (edgeNodes, numRepeatNodes, newMeshes, meshes, nodeId, eleId);
    secMesh.UpdateEdgeNodes (newEdgeNodes, numRepeatNodes, edgeNodes);

    // 3rd block
    point.m_z = h;
    secMesh.MeshRect2 (point, t1, -0.5 * (h - t2), Na1, Nb2, nodeId, eleId, _OUT newMeshes, _OUT newEdgeNodes);
    numRepeatNodes = 2 * Na1 + 1;
    secMesh.Merge (edgeNodes, numRepeatNodes, newMeshes, meshes, nodeId, eleId);
    secMesh.UpdateEdgeNodes (newEdgeNodes, numRepeatNodes, edgeNodes);

    // 4th block
    point.m_y = 0.0;
    point.m_z = 0.5 * (h - t2);
    secMesh.MeshRect (point, 0.5 * w, t2, Na2, Nb1, nodeId, eleId, _OUT newMeshes, _OUT newEdgeNodes);
    numRepeatNodes = 2 * Nb1 + 1;
    secMesh.Merge (edgeNodes, numRepeatNodes, newMeshes, meshes, nodeId, eleId);
    secMesh.UpdateEdgeNodes (newEdgeNodes, numRepeatNodes, edgeNodes);

    // 5th block
    point.m_y = w + t1;
    secMesh.MeshRect (point, -0.5 * w, t2, Na2, Nb1, nodeId, eleId, _OUT newMeshes, _OUT newEdgeNodes);
    numRepeatNodes = 2 * Nb1 + 1;
    secMesh.Merge (edgeNodes, numRepeatNodes, newMeshes, meshes, nodeId, eleId);
}